﻿namespace MetaDrive.VisualStudio
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using EnvDTE;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell.Interop;

    public static class ProjectItemExtensions
    {        
        public static void SetPropertyValue(this ProjectItem projectItem, string propertyName, object value)
        {
            Property property = projectItem.Properties.Item(propertyName);
            if (property == null)
            {
                throw new Exception(
                    string.Format(CultureInfo.CurrentCulture, "Property {0} is not supported for {1}", propertyName, projectItem.Name));
            }

            property.Value = value;
        }
                      
        public static void SetProjectItemBuildProperties(this ProjectItem projectItem, IDictionary<string, string> buildProperties)
        {
            IServiceProvider serviceProvider = projectItem.DTE.GetServiceProvider();

            if (buildProperties.Count > 0)
            {
                // Get access to the build property storage service                
                IVsSolution solution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
                IVsHierarchy hierarchy;
                ErrorHandler.ThrowOnFailure(solution.GetProjectOfUniqueName(projectItem.ContainingProject.UniqueName, out hierarchy));

                IVsBuildPropertyStorage buildPropertyStorage = hierarchy as IVsBuildPropertyStorage;
                if (buildPropertyStorage == null)
                {
                    throw new Exception(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            "Project {0} does not support build properties required by {1}",
                            projectItem.ContainingProject.Name,
                            projectItem.Name));
                }

                // Find the target project item in the property storage
                uint projectItemId;
                ErrorHandler.ThrowOnFailure(hierarchy.ParseCanonicalName(projectItem.get_FileNames(1), out projectItemId));

                // Set build projerties for the target project item
                foreach (KeyValuePair<string, string> buildProperty in buildProperties)
                {
                    ErrorHandler.ThrowOnFailure(buildPropertyStorage.SetItemAttribute(projectItemId, buildProperty.Key, buildProperty.Value));
                }
            }
        }

        /// <summary>
        /// Deletes the specified <paramref name="item"/> and its parent folders if they are empty.
        /// </summary>
        /// <param name="item">A Visual Studio <see cref="ProjectItem"/>.</param>
        /// <remarks>
        /// This method correctly deletes empty parent folders in C# and probably 
        /// Visual Basic projects which are implemented in C++ as pure COM objects. 
        /// However, for Database and probably WiX projects, which are implemented 
        /// as .NET COM objects, the parent collection indicates item count = 1 
        /// even after its only child item is deleted. So, for new project types,
        /// this method doesn't delete empty parent folders. However, this is probably
        /// desirable for Database projects that create a predefined, empty folder 
        /// structure for each schema. We may need to solve this problem in the 
        /// future by recording which folders were actually created by the code 
        /// generator in the log file and deleting the empty parent folders when 
        /// the previously generated folders become empty.
        /// </remarks>
        public static void DeleteRecursive(this ProjectItem item)
        {
            ProjectItems parentCollection = item.Collection;
            item.Delete();

            if (parentCollection.Count == 0)
            {
                ProjectItem parent = parentCollection.Parent as ProjectItem;
                if (parent != null && parent.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder)
                {
                    DeleteRecursive(parent);
                }
            }
        }

        /// This method is necessary for MPF-based project implementations, such as database projects, which can return different 
        /// ProjectItems instances ultimately pointing to the same folder.
        public static bool Same(this ProjectItems collection1, ProjectItems collection2)
        {
            if (collection1 == collection2)
            {
                return true;
            }

            ProjectItem parentItem1 = collection1.Parent as ProjectItem;
            ProjectItem parentItem2 = collection2.Parent as ProjectItem;

            if (parentItem1 != null && parentItem2 != null)
            {
                if (string.Compare(parentItem1.Name, parentItem2.Name, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    return false;
                }

                return Same(parentItem1.Collection, parentItem2.Collection);
            }

            Project parentProject1 = collection1.Parent as Project;
            Project parentProject2 = collection2.Parent as Project;

            if (parentProject1 != null && parentProject2 != null)
            {
                return string.Compare(parentProject1.FullName, parentProject2.FullName, StringComparison.OrdinalIgnoreCase) == 0;
            }

            return false;
        }
    }
}
